package rafael.bot;

import java.util.Random;

public class NaiveBayesModel extends Model {

    private double priori[];
    private Random rand;
    private int k;
    private int f[][][];

    public NaiveBayesModel(int memoryDepth) {
        k = memoryDepth;
    }

    @Override
    public void init(int movs) {
        rand = new Random();
        priori = new double[3];
        for (int i = 0; i < priori.length; i++) {
            priori[i] = 1.0 / 3.0;
        }
        int inputLength = k * 2;
        f = new int[3][3][inputLength];
    }

    @Override
    public void learn(int[] input, int output, double learningRate) {
        for (int i = 0; i < input.length; i++) {
            if (input[i] == 0) {
                return;
            }
        }
        int y = output - 1;
        priori[y]++;
        priori = normalSum(priori);
        for (int i = 0; i < input.length; i++) {
            f[y][input[i] - 1][i]++;
        }
    }

    @Override
    public int predictMove(int[] input) {
        for (int i = 0; i < input.length; i++) {
            if (input[i] == 0) {
                return rand.nextInt(3) + 1;
            }
        }
        int predictedMove = 0;
        double[] p = new double[priori.length];
        for (int i = 0; i < priori.length; i++) {
            p[i] = Math.exp(Math.log(priori[i]) + posteriori(input, i));
        }

        p = normalSum(p);
//		double ac = 0;
//		double u = rand.nextDouble();
//		for(int i = 0; i < p.length; i++) {
//			ac += p[i];
//			if(u < ac) {
//				predictedMove = i;
//				break;
//			}
//		}
        double max = 0;
        for (int i = 0; i < p.length; i++) {
            if (p[i] > max) {
                predictedMove = i;
                max = p[i];
            }
        }
        return predictedMove + 1;
    }

    // P(X|Y)
    private double posteriori(int[] x, int y) {
        int n = x.length;
        double sum = 0;
        for (int i = 0; i < n; i++) {
            sum += Math.log(p(x, i, y));
        }
        return sum;
    }

    // P(X[i] at i | y)
    private double p(int[] x, int i, int y) {
        int n = x.length;
        return (f[y][x[i] - 1][i] + 1.0) / (n + 3.0);
    }

    private double[] normalSum(double[] v) {
        double size = 0;
        for (int i = 0; i < v.length; i++) {
            size += v[i];
        }
        for (int i = 0; i < v.length; i++) {
            v[i] /= size;
        }
        return v;
    }

}
